-- UISkyActivity
-- Create by chendh
-- 天空活动界面

-- UISkyActivity继承自Layer
UISkyActivity = class("UISkyActivity", function()
    return cc.Layer:create();
end);

function UISkyActivity.create(activityId)
    return UISkyActivity.new(activityId);
end

local MAX_OFFSET    = 600;
local EXTRA_OFFSET  = 50;
local SHOW_NUM      = 6;
local totalOffset   = 0;
local isBouncing    = false;
local moveSpeed     = {
    ["airship"] = 0.7,
    ["combat1"] = 1.05,
    ["combat2"] = 0.98,
    ["combat3"] = 0.84,
    ["combat4"] = 0.77,
    ["combat5"] = 0.7,
    ["combat6"] = 0.84,
    ["combat7"] = 0.98,
}

-- 内部函数声明

-- 构造函数
function UISkyActivity:ctor(activityId)
    -- 初始化
    self:setName("UISkyActivity");
    local node = cc.CSLoader:createNode("layout/sky_city/SkyActivity.csb");
    self:addChild(node);
    self.node       = node;
    self.panel      = findChildByName(self.node, "CT/panel");

    self.activityId = activityId;
    totalOffset     = 0;
    isBouncing      = false;

    -- 注册事件处理回调函数
    self:registerEventCallback();

    -- 注册点击事件
    self:registerTouchEvent();

    -- 初始化数据
    self:initData();

    -- 重绘
    self:redraw(true);

    -- 适配
    self:resize();

    -- 出现效果
    self:playAppearEffect();
end

-- 适配
function UISkyActivity:resize()
    local node = self.node;

    -- bg居中显示并充满整个屏幕
    AlignM.fitToScreen(node, "bg");
    AlignM.fitToScreen(node, "bg2");

    -- TOP顶部居中并缩放至合适大小
    AlignM.alignToTopCenter(node, "TOP");

    -- CT居中并缩放至合适大小
    AlignM.alignToCenter(node, "CT");

    -- BT置于底部并缩放至合适大小
    AlignM.alignToBottomCenter(node, "BT");
end

-- 初始化奖励说明数据
function UISkyActivity:initData()
    local showList      = {};
    local activityConfig = SkyActivityM.query(self.activityId);
    local combatList    = activityConfig["combat"];
    local combatNum     = #combatList;

    for index, combatId in ipairs(combatList) do
        if index <= SHOW_NUM then
            table.insert(showList, combatId);
        else
            -- 超过了显示范围的，默认是后面几场被击杀后变身的
            local preIndex = index + SHOW_NUM - combatNum;
            local preId = showList[preIndex];

            if SkyActivityM.getCombatStatus(self.activityId, preId) == "is_die" then
                showList[preIndex] = combatId;
            end
        end
    end

    self.showList = showList;
end

-- 显示
function UISkyActivity:redraw(isFirstRedraw)
    -- 显示标题
    local titleLabel = findChildByName(self.node, "TOP/title_bar/title");
    TextStyleM.setTitleStyle(titleLabel);
    titleLabel:setString(SkyActivityM.query(self.activityId, "name"));

    -- 绘制倒计时
    self:redrawTime();

    -- 如果活动已结束，则隐藏所有的战斗，显示剩余兑换时间
    local status = ActivityM.getActivityStatus(ActivityM.getActivityInfo(self.activityId));
    if status == ACTIVITY_STATUS_END or status == ACTIVITY_STATUS_CLOSE then
        -- 活动已结束或者关闭的情况下，不显示panel
        self.panel:setVisible(false);

        for index=1, SHOW_NUM do
            local combatNode = findChildByName(self.node, "CT/panel/combat" .. index);
            combatNode:setVisible(false);
        end

        -- 绘制剩余兑换时间
        local function tick()
            local remainTime = ActivityM.getRemainRunningTime(self.activityId);
            local timeDesc = remainingTimeDesc(remainTime);
            local endLabel = findChildByName(self.node, "CT/end_text");
            TextStyleM.setTextStyle(endLabel, TextStyleM.TEXT_SIZE_MIDDLE2, TextStyleM.TEXT_COLOR_ORANGE, true);
            endLabel:setString(string.format(getLocStr("sky_activity_end_tip"), timeDesc));

            performWithDelay(self, tick, 1);
        end

        tick();
        return;
    end

    -- 云层浮动效果
    local cloud1 = findChildByName(self.node, "CT/panel/airship/cloud/cloud1");
    local cloud2 = findChildByName(self.node, "CT/panel/airship/cloud/cloud2");
    local cloud3 = findChildByName(self.node, "CT/panel/airship/cloud/cloud3");
    self:playFloatingCloudEffect(cloud1);
    self:playFloatingCloudEffect(cloud2);
    self:playFloatingCloudEffect(cloud3);

    local showHideBoss = SkyActivityM.canViewHideBoss(self.activityId);
    local hideBossNode = findChildByName(self.node, "CT/panel/hide_boss");
    if showHideBoss then
        -- 显示隐藏BOSS
        hideBossNode:setVisible(true);

        -- 绘制BOSS
        local combatNum = #self.showList;

        local opacity = {255, 220, 220, 200, 200, 170};
        local scale   = {0.7, 0.5, 0.5, 0.4, 0.4, 0.4};
        local showNum = math.min(combatNum, 6);
        for index=1, showNum do
            repeat
                local node = findChildByName(hideBossNode, "combat" .. index);

                if not node then
                    break;
                end

                node:removeAllChildren();
                local monsterIcon = ccui.ImageView:create();
                local monsterNode = cc.Node:create();
                node:addChild(monsterIcon);
                node:addChild(monsterNode);

                -- 创建怪物模型
                local combatId = self.showList[showNum + 1 - index];
                local monsterId = SkyActivityM.queryCombat(combatId, "class_id");
                local petInfo = PetM.queryModel(PetM.query(monsterId, "default_model"));
                local iconPath = getHeroIconPath(PetM.getDefaultIcon(monsterId));

                local display = createHeroModel(monsterId, monsterIcon, monsterNode,
                    petInfo["model_scale"], iconPath, petInfo["offset_x"], petInfo["offset_y"]);
                monsterNode.display = display;
                monsterNode:setScale(scale[index]);
                display:setOpacity(opacity[index]);
            until true
        end

        local hideCombat = SkyActivityM.query(self.activityId, "hide_combat");
        local status = SkyActivityM.getCombatStatus(self.activityId, hideCombat);

        -- 点击处理
        local function onItemClick(sender, eventType)
            if eventType == ccui.TouchEventType.ended then
                -- 如果是锁定或者死亡状态，显示提示语
                local status = SkyActivityM.getCombatStatus(self.activityId, hideCombat);
                if status == "lock" then
                    alert(SkyActivityM.queryCombat(hideCombat, "condition_tip"));
                    return;
                elseif status == "is_die" then
                    alert(getLocStr("sky_activity_die_tip"));
                    return;
                end

                require "game/ui/form/sky_city/UISkyActivityCombatInfo"
                local uiForm = UISkyActivityCombatInfo.create(self.activityId, hideCombat);
                UIMgr.getCurrentScene():addForm(uiForm);
            end
        end

        -- 活动隐藏BOSS背景
        local bg = findChildByName(self.node, "CT/panel/hide_boss/bg");
        bg:setTouchEnabled(true);
        bg:addTouchEventListener(onItemClick);

        if status == "open" then
            --playSkyActivityHideBossEffecy(activityBg);
            -- 绘制隐藏BOSS
        elseif status == "is_die" then
            turnFormGray(hideBossNode, true);

            --[[if activityBg.imageView then
                activityBg.imageView = nil;
                activityBg.imageView:removeFromParent();
            end
            activityBg:stopAllActions();]]
        end
    else
        hideBossNode:setVisible(false);
    end

    -- 绘制战斗item
    local combatNum = #self.showList;
    for index=1, SHOW_NUM do
        local combatNode = findChildByName(self.node, "CT/panel/combat" .. index);
        local itemNode = findChildByName(combatNode, "item");
        if self:isNeedShow(combatNode) then
            combatNode.isShow = true;
            itemNode:setScale(1);
            itemNode:setOpacity(255);
        else
            combatNode.isShow = false;
            itemNode:setScale(0.01);
            itemNode:setOpacity(0);
        end

        if showHideBoss then
            itemNode:setVisible(false);
        elseif index <= combatNum then
            itemNode:setVisible(true);
            -- 上下悬浮

            -- 先将combatNode恢复到初始的Y坐标
            if not combatNode.fluctuateAction then
                performWithDelay(combatNode, function ()
                    playFluctuateAnima(combatNode, 1.5, 8);
                end, math.random(2000) / 1000);
            end

            local bgImg         = findChildByName(combatNode, "item/item_bg");
            local statusImg     = findChildByName(combatNode, "item/status");
            local iconImg       = findChildByName(combatNode, "item/icon");
            local monsterNode   = findChildByName(combatNode, "item/monster");
            local combatId      = self.showList[index];
            local combatConfig  = SkyActivityM.queryCombat(combatId);
            local monsterId     = combatConfig["class_id"];
            local status        = SkyActivityM.getCombatStatus(self.activityId, combatId);

            local activityData = SkyActivityM.getActivityData(self.activityId);
            local combatInfo = activityData["combat"] or {};
            local combatData = combatInfo[combatId] or {};

            -- 创建怪物模型
            local display;
            if combatData["dungeon_eggs"] and not combatData["dungeon_status"] then
                -- 触发了地牢彩蛋，绘制一个传送门

                -- 如果需要播放变身光效，则在播放完光效后再绘制传送门
                if not isFirstRedraw and not monsterNode.dungeonEggs then
                    monsterNode:removeAllChildren();

                    iconImg:setVisible(false);
                    monsterNode:setVisible(true);
                    monsterNode:setScale(1);
                    -- 播放传送门的漩涡光效
                    playEffect(monsterNode, 1102, -65, 85, nil, 1.2);
                    -- 播放变身的烟尘光效
                    playEffect(monsterNode, 1320, nil, -30);

                    performWithDelay(monsterNode, function ()
                        -- %s逃进了飞船！
                        alert(string.format(getLocStr("sky_activity_combat_run_away"), PetM.query(monsterId, "name")));
                    end, 0.2);
                else
                    monsterNode:removeAllChildren();
                    iconImg:setVisible(false);
                    monsterNode:setVisible(true);
                    monsterNode:setScale(1);
                    playEffect(monsterNode, 1102, -65, 85, nil, 1.2);
                end

                monsterNode.dungeonEggs = combatData["dungeon_eggs"];
            else
                local modelId = PetM.query(monsterId, "default_model");
                local petInfo = PetM.queryModel(modelId);
                local iconPath = getHeroIconPath(PetM.getDefaultIcon(monsterId));

                if combatConfig["shape_id"] > 0 then
                    local shapeId = combatConfig["shape_id"];
                    local shapeInfo = ShapeChangeM.query(shapeId);
                    iconPath = getHeroIconPath(shapeInfo["icon"]);
                    local animation = shapeInfo["animation"];

                    -- 如果需要播放变身光效，则在播放完光效后重绘怪物
                    if not isFirstRedraw and monsterNode.monsterId and
                        monsterNode.shape_id ~= combatConfig["shape_id"] then
                        display = monsterNode.display;

                        local function frameEventFunc(frame)
                            if frame.getEvent and frame:getEvent() == "pilor" then
                                local index = frame:getFrameIndex();

                                -- 光效播放完了，刷新怪物模型
                                if index == 55 then
                                    monsterNode:removeAllChildren();
                                    display = createShapeChangeMode(shapeId, iconImg, animation, monsterNode,
                                        shapeInfo["model_scale"], iconPath, shapeInfo["offset_x"], shapeInfo["offset_y"]);

                                end
                            end
                        end
                        playEffect(monsterNode, 1332, nil, nil, frameEventFunc);
                    else
                        monsterNode:removeAllChildren();
                        display = createShapeChangeMode(shapeId, iconImg, animation, monsterNode,
                            shapeInfo["model_scale"], iconPath, shapeInfo["offset_x"], shapeInfo["offset_y"]);
                    end

                    monsterNode.shape_id = combatConfig["shape_id"];
                else
                    monsterNode:removeAllChildren();
                    display = createHeroModel(modelId, iconImg, monsterNode,
                        petInfo["model_scale"], iconPath, petInfo["offset_x"], petInfo["offset_y"]);
                end
                monsterNode.display = display;
                monsterNode:setScale(0.8);
                monsterNode.monsterId = monsterId;
            end

            -- 根据状态设置图标
            if status == "lock" then
                statusImg:setVisible(true);
                statusImg:loadTexture(getSkyAcitivityPath("lock"));

                if display then
                    turnFormGray(display, false);
                end

                -- 战斗目标背景设置不透明
                bgImg:setOpacity(255);
            elseif status == "is_die" and (not combatData["dungeon_eggs"] or combatData["dungeon_status"]) then
                statusImg:setVisible(true);
                statusImg:loadTexture(getSkyAcitivityPath("kill"));

                -- 已击杀怪物节点去色
                if display then
                    turnFormGray(display, true);
                end

                -- 战斗目标背景加透明度
                bgImg:setOpacity(150);
            else
                statusImg:setVisible(false);

                if display then
                    turnFormGray(display, false);
                end

                -- 战斗目标背景设置不透明
                bgImg:setOpacity(255);
            end

            -- 点击处理
            local function onItemClick(sender, eventType)
                if eventType == ccui.TouchEventType.ended then
                    -- 如果是锁定或者死亡状态，显示提示语
                    local status = SkyActivityM.getCombatStatus(self.activityId, combatId);

                    if combatData["dungeon_eggs"] then
                        if combatData["dungeon_status"] == "pass" then
                            alert(getLocStr("sky_activity_die_tip"));
                        elseif combatData["dungeon_status"] == "leave" then
                            alert(getLocStr("sky_activity_run_away_tip"));
                        else
                            -- 该战斗触发了地牢彩蛋，点击时打开地牢进入界面
                            local dungeonId = SkyActivityM.queryDungeonEggs(combatData["dungeon_eggs"], "dungeon_id");

                            -- 打开关卡进入界面
                            UIMgr:getCurrentScene():removeFormByName("UIEnterDungeon");
                            local uiEnterDungeon = UIEnterDungeon.create(dungeonId);
                            UIMgr.getCurrentScene():addForm(uiEnterDungeon);
                            AudioM.playFx("ui_open1");
                        end
                    elseif status == "lock" then
                        alert(SkyActivityM.queryCombat(combatId, "condition_tip"));
                        return;
                    elseif status == "is_die" then
                        alert(getLocStr("sky_activity_die_tip"));
                        return;
                    else
                        require "game/ui/form/sky_city/UISkyActivityCombatInfo"
                        local uiForm = UISkyActivityCombatInfo.create(self.activityId, combatId);
                        UIMgr.getCurrentScene():addForm(uiForm);
                    end

                    -- 记录停止滑动的时间
                    sender.endMoveTime = socket.gettime();

                    local movePos = sender:getTouchMovePosition();
                    sender.lastMoveX = sender.initMoveX;
                    sender.initMoveX = movePos.x;

                    -- 移动结束的处理
                    self:moveEnd(sender);

                    sender.initMoveX = nil;
                    sender.lastMoveX = nil;
                elseif eventType == ccui.TouchEventType.began then
                    sender.initMoveX = nil;
                    sender.lastMoveX = nil;

                    self.moveTime = socket.gettime();
                    -- 记录开始滑动的时间
                    sender.startMoveTime = socket.gettime();
                elseif eventType == ccui.TouchEventType.moved then
                    local movePos = sender:getTouchMovePosition();
                    if not sender.initMoveX then
                        sender.initMoveX = movePos.x;
                    end

                    self.moveTime = socket.gettime();
                    -- 移动飞行器
                    local offset = sender.initMoveX - movePos.x;

                    sender.lastMoveX = sender.initMoveX;
                    sender.initMoveX = movePos.x;
                    self:moveItems(-offset);
                end
            end
            bgImg:addTouchEventListener(onItemClick);
        else
            itemNode:setVisible(false);
        end
    end
end

-- 绘制时间
function UISkyActivity:redrawTime()
    if self.isTick then
        return;
    end
    local function tick()
        -- 提示信息
        local activityInfo = ActivityM.getActivityInfo(self.activityId);
        local leftTime = ActivityM.getRemainTime(self.activityId);
        local txt1Label = findChildByName(self.node, "TOP/subhead");

        local serverTime = TimeM.getServerTime();
        local refreshTime = activityInfo["daily_reset_time"] - serverTime;
        if refreshTime <= 0 then
            refreshTime = 0;
        end

        if leftTime <= 0 then
            txt1Label:setVisible(false);
            return;
        end

        TextStyleM.setSubheadStyle(txt1Label);

        if activityInfo["end_time"] > activityInfo["daily_reset_time"] then
            txt1Label:setString(string.format(getLocStr("ranking_list_reset_time"), remainingTimeDesc(refreshTime)));
        else
            txt1Label:setString(string.format(getLocStr("sky_activity_bonus_time"), remainingTimeDesc(leftTime)));
        end

        performWithDelay(self, tick, 1);
    end
    self.isTick = true;
    tick();
end

-- 注册事件处理回调函数
function UISkyActivity:registerEventCallback()
    -- 关注活动关闭事件
    EventMgr.register("UISkyActivity", event.ACTIVITY_CLOSE, function(args)
        -- 如果是当前活动关闭了，那么退出界面
        if self.activityId == args["id"] then
            -- 关闭当前界面
            AudioM.playFx("ship_return");
            UIMgr.getCurrentScene():removeFormByName("UISkyActivity");
        end
    end);

    -- 关注活动阶段变化事件
    EventMgr.register("UISkyActivity", event.ACTIVITY_CHANGE, function(args)
        -- 如果是当前活动阶段变化了，那么刷新界面
        if self.activityId == args["id"] then
            -- 如果当前焦点窗口是自己，直接刷新，否则记录下来等获得焦点时刷新
            if UIMgr.getCurrentScene():getCurrentForm():getName() == "UISkyActivity" then
                self:initData();
                self:redraw();
            else
                self.needRedraw = true;
            end
        end
    end);

    -- 注册获得焦点的回调
    EventMgr.register("UISkyActivity", event.FOCUS_CHANGED, function(para)
        if para["get"] == "UISkyActivity" then
            -- 重新请求下活动信息
            Operation.cmd_request_activity_list();
            -- 重新请求下属性
            Operation.cmd_request_attrib("sky_activity");

            -- TopMenu显示天空活动的数据和袭击次数
            local uiTopMenu = UIMgr:getCurrentScene().topMenu;
            uiTopMenu:setSkyActivityId(self.activityId);
            uiTopMenu:show(TOPMENU_FRAME_ACTIVITY_DATA, TOPMENU_FRAME_ACTIVITY_TIMES, TOPMENU_FRAME_HEAD_AIRSHIP);

            -- 显示界面
            self:setVisible(true);

            if type(self.extraData) == "table" then
                local dungeonEggsId = self.extraData["dungeon_eggs"];
                local killEggsId = self.extraData["kill_eggs"];

                self.extraData = nil;
                if dungeonEggsId and dungeonEggsId > 0 then
                    -- 触发了地牢彩蛋
                    local templateId = SkyActivityM.queryDungeonEggs(dungeonEggsId, "diaglue_template");

                    require "game/ui/form/main/UIDialogue";
                    local uiDialog = UIDialogue.create(templateId);
                    UIMgr.getCurrentScene():addForm(uiDialog);
                    return;
                elseif killEggsId and killEggsId > 0 then
                    -- 触发了击杀彩蛋
                    local templateId = SkyActivityM.queryKillEggs(killEggsId, "diaglue_template");
                    local medalNum = SkyActivityM.queryKillEggs(killEggsId, "medal");

                    local function callback()
                        -- 对白结束回调，提示获得勋章
                        local medalClassId  = SkyActivityM.query(self.activityId, "medal_class_id");
                        showBonusNotifyForm(nil, {{1, medalClassId, medalNum}});
                    end

                    require "game/ui/form/main/UIDialogue";
                    local uiDialog = UIDialogue.create(templateId, callback);
                    UIMgr.getCurrentScene():addForm(uiDialog);
                    return;
                end
            end

            if self.needRedraw then
                self.needRedraw = nil;

                self:initData();
                self:redraw();
            end

        elseif para["lose"] == "UISkyActivity" then
            -- 隐藏界面
            if para["get"] ~= "UIDialogue" then
                self:setVisible(false);
            end
        end
    end);

    -- 关注玩家属性变化
    EventMgr.register("UISkyActivity", event.USER_INFO_UPDATED, function(para)
        if para == "sky_activity" then
            self.needRedraw = true;

            -- 如果当前焦点窗口是自己，直接刷新，否则记录下来等获得焦点时刷新
            if UIMgr.getCurrentScene():getCurrentForm():getName() == "UISkyActivity" then
                self:initData();
                self:redraw();
            else
                self.needRedraw = true;
            end
        end
    end);

    -- 注册参与活动结果的回调
    EventMgr.register("UISkyActivity", event.SKY_ACTIVITY_RESULT, function(para)
        -- 缓存信息
        local extraData = para["extra_data"];
        self.extraData = extraData;
    end);

    -- 界面析构后，需要清理下
    self:registerScriptHandler(function(eventType)
        if eventType == "exit" then
            EventMgr.removeAll("UISkyActivity");
        end
    end);
end

-- 注册点击事件
function UISkyActivity:registerTouchEvent()
    -- 注册奖励说明按钮点击事件
    local btnBonus = findChildByName(self.node, "BT/btn_bonus");
    local function onBonusClick(sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            require "game/ui/form/sky_city/UISkyActivityBonus"
            local uiForm = UISkyActivityBonus.create(self.activityId);
            UIMgr.getCurrentScene():addForm(uiForm);
        end
    end
    btnBonus:addTouchEventListener(onBonusClick);
    TextStyleM.setTextStyle(btnBonus, TextStyleM.TEXT_SIZE_MIDDLE, TextStyleM.TEXT_COLOR_BROWN_YELLOW, true);
    btnBonus:setTitleText(getLocStr("btn_text_sky_activity_bonus"));

    -- 注册返回按钮点击事件
    local btnBack = findChildByName(self.node, "BT/btn_back");
    local function onBackClick(sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            -- 关闭当前界面
            AudioM.playFx("ship_return");
            UIMgr.getCurrentScene():removeFormByName("UISkyActivity");
        end
    end
    AddBackEventHandler(self, btnBack, onBackClick);
    TextStyleM.setTextStyle(btnBack, TextStyleM.TEXT_SIZE_MIDDLE, TextStyleM.TEXT_COLOR_BROWN_YELLOW, true);
    btnBack:setTitleText(getLocStr("btn_text_back"));

    -- panel点击处理
    local function onPanelClick(sender,eventType)
        if self.isMoveing then
            return;
        end

        if eventType == ccui.TouchEventType.ended then
            -- 记录结束滑动的时间
            sender.endMoveTime = socket.gettime();

            -- 移动结束的处理
            self:moveEnd(sender);

            sender.initMoveX = nil;
            sender.lastMoveX = nil;
        elseif eventType == ccui.TouchEventType.began then
            sender.initMoveX = nil;
            sender.lastMoveX = nil;

            self.moveTime = socket.gettime();
            -- 记录开始滑动的时间
            sender.startMoveTime = socket.gettime();
        elseif eventType == ccui.TouchEventType.moved then
            local movePos = sender:getTouchMovePosition();
            if not sender.initMoveX then
                sender.initMoveX = movePos.x;
            end

            self.moveTime = socket.gettime();
            -- 移动飞行器
            local offset = sender.initMoveX - movePos.x;
            sender.lastMoveX = sender.initMoveX;
            sender.initMoveX = movePos.x;
            self:moveItems(-offset);
        end
    end

    self.panel:addTouchEventListener(onPanelClick);
end

-- 出现效果
function UISkyActivity:playAppearEffect()
    local combatNum = #self.showList;
    for index=1, SHOW_NUM do
        local combatNode = findChildByName(self.node, "CT/panel/combat" .. index);
        if index <= combatNum then
            combatNode:setScale(0.01);

            local delay = cc.DelayTime:create(0.1 * index);
            local scaleDown1 = cc.ScaleTo:create(0.2, 0.95);
            local scaleUp1 = cc.ScaleTo:create(0.2, 1.05);
            local scaleDown2 = cc.ScaleTo:create(0.1, 0.95);

            combatNode:runAction(cc.Sequence:create(delay, scaleDown1, scaleUp1, scaleDown2));
        end
    end
end

-- 判断容器中指定节点是否需要显示
function UISkyActivity:isNeedShow(node)
    local panel         = self.panel;
    local scrollSize    = panel:getContentSize();
    local posX          = node:getPositionX();
    local monsterNode   = findChildByName(node, "item/monster");
    local monsterPosX   = monsterNode:getPositionX();

    local min = (scrollSize.width - DESIGN_WIDTH) / 2;
    local max = min + DESIGN_WIDTH;

    if posX >= min and posX <= max then
        if posX + monsterPosX >= min and posX + monsterPosX <= max then
            return true;
        else
            return false;
        end
    else
        return false;
    end

end

-- 移动飞行器
function UISkyActivity:moveItems(offset)
    local panel = self.panel;

    -- 如果正在回弹，则不移动
    if isBouncing then
        return;
    end

    -- 如果累计移动范围超过了设定还在继续往同一方向移动，那么停止移动
    if math.abs(totalOffset) > MAX_OFFSET + EXTRA_OFFSET and totalOffset * offset > 0 then
        return;
    end

    totalOffset = totalOffset + offset;

    for _, node in ipairs(panel:getChildren()) do
        local name = node:getName();
        local len = offset * (moveSpeed[name] or moveSpeed["airship"]);

        --[[local moveBy = cc.MoveBy:create(0.03, cc.p(len, 0));
        local callfunc = cc.CallFunc:create(function ()
            self:doAppeareEffect(node);
        end);

        node:runAction(cc.Sequence:create(moveBy, callfunc));]]
        local posX = node:getPositionX();
        node:setPositionX(posX + len);
        self:doAppeareEffect(node);
    end
end

-- 处理战斗节点的出现和消失效果
function UISkyActivity:doAppeareEffect(node)
    local itemNode = findChildByName(node, "item");
    if not itemNode then
        return;
    end

    local isShow = self:isNeedShow(node);
    if isShow and not node.isShow then
        -- 原先不显示，现在要显示，播放出现效果
        if node.showAction then
            node:stopAction(node.showAction);
        end
        node.isShow = isShow;

        -- 播放怪物气泡出现效果
        local scaleTo = cc.ScaleTo:create(0.2, 1);
        local fadeIn = cc.FadeIn:create(0.2);
        local callFunc = cc.CallFunc:create(function ()
            node.showAction = nil;
        end)
        node.showAction = itemNode:runAction(cc.Sequence:create(cc.Spawn:create(scaleTo, fadeIn), callFunc));

    elseif not isShow and node.isShow then
        -- 原先显示， 现在不显示，播放消失效果
        if node.showAction then
            node:stopAction(node.showAction);
        end
        node.isShow = isShow;

        -- 播放怪物气泡消失效果
        local scaleTo = cc.ScaleTo:create(0.2, 0.01);
        local fadeOut = cc.FadeOut:create(0.2);
        local callFunc = cc.CallFunc:create(function ()
            node.showAction = nil;
        end)
        node.showAction = itemNode:runAction(cc.Sequence:create(cc.Spawn:create(scaleTo, fadeOut), callFunc));
    end
end

-- 移动结束的处理
function UISkyActivity:moveEnd(node)
    if math.abs(totalOffset) < MAX_OFFSET then
        -- 还没到边界线，那么继续惯性移动
        self:keepMove(node);
    else
        -- 已经超过了最大位移了，那么尝试回弹
        self:tryToBounce();
    end
end

-- 惯性移动
function UISkyActivity:keepMove(node)
    -- 如果正在回弹，则不移动
    if isBouncing then
        return;
    end

    local panel = self.panel;

    local startTime = node.startMoveTime;
    local endTime   = node.endMoveTime;
    local beganPos  = node:getTouchBeganPosition().x;
    local endPos    = node:getTouchEndPosition().x;

    -- 拖动不超过10像素就不处理了
    if math.abs(endPos - beganPos) < 10 or
        node.initMoveX == node.lastMoveX then
        return;
    end

    local speed     = math.abs(endPos - beganPos) / (endTime - startTime);
    local acc       = 5000;
    local delay     = speed / acc;
    local direction = (endPos - beganPos) / math.abs(endPos - beganPos);
    local distance  = (speed * delay + acc * delay * delay / 2) * direction * 2;

    -- 惯性移动不超过屏幕宽度的2分之一
    if math.abs(distance) > AlignM.frameSize.width / 2 then
        distance = AlignM.frameSize.width / 2 * distance / math.abs(distance);
    end

    if socket.gettime() - self.moveTime >= 0.2 then
        distance = distance / 3;
    end

    totalOffset = totalOffset + distance;

    for _, node in ipairs(panel:getChildren()) do
        local name = node:getName();
        local len  = distance * (moveSpeed[name] or moveSpeed["airship"]);

        local moveBy = cc.MoveBy:create(delay, cc.p(len, 0));

        node:runAction(cc.EaseOut:create(cc.Sequence:create(moveBy), 2.5));

        -- 计算这个节点多长时间之后会需要播放消失效果
        local delayTime = cc.DelayTime:create(0.03);
        local callfunc = cc.CallFunc:create(function ()
            self:doAppeareEffect(node);
        end)

        node:runAction(cc.Repeat:create(cc.Sequence:create(delayTime, callfunc), math.ceil(delay / 0.03)));
    end

    -- 惯性移动结束后尝试回弹
    performWithDelay(panel, function ()
        self:tryToBounce();
    end, delay);
end

-- 回弹效果
function UISkyActivity:tryToBounce()
    if math.abs(totalOffset) < MAX_OFFSET then
        return;
    end

    -- 标记回弹状态
    isBouncing = true;

    local panel = self.panel;
    local delay = 0.1;
    local direction = totalOffset / math.abs(totalOffset);
    local redundantOffset = totalOffset - MAX_OFFSET * direction;

    -- 回弹距离不要超过屏幕宽度的3分之一
    if math.abs(redundantOffset) > AlignM.frameSize.width / 3 then
        redundantOffset = AlignM.frameSize.width / 3 * redundantOffset / math.abs(redundantOffset);
    end

    for _, node in ipairs(panel:getChildren()) do
        local name = node:getName();
        local len = redundantOffset * (moveSpeed[name] or moveSpeed["airship"]);

        local moveBy = cc.MoveBy:create(delay, cc.p(-len, 0));
        local callFunc = cc.CallFunc:create(function ()
            self:doAppeareEffect(node);
        end)
        node:runAction(cc.EaseOut:create(cc.Sequence:create(moveBy, callFunc), 2.5));
    end

    -- 回弹结束后设置当前不在回弹状态
    performWithDelay(panel, function ()
        isBouncing = false;
        totalOffset = totalOffset - redundantOffset;
    end, delay);
end

-- 浮动的云层效果
function UISkyActivity:playFloatingCloudEffect(node)
    node:stopAllActions();

    local posX  = node:getPositionX();
    local width = 800 + node:getContentSize().width * node:getScaleX();
    local len   = 0;

    if posX > 0 then
        len = posX + width;
    else
        len = posX - width;
    end

    local delay = math.abs(len) / math.random(10, 30);
    local moveBy = cc.MoveBy:create(delay, cc.p(-len, 0));
    local callFunc = cc.CallFunc:create(function ()
        self:playFloatingCloudEffect(node);
    end);
    local delayTime = cc.DelayTime:create(math.random(1, 10));

    node:runAction(cc.Sequence:create(moveBy, delayTime, callFunc));
end